'use strict';
import * as path from 'path';
import * as vscode from 'vscode';
import * as ctags from './ctags';
import * as util from './util';
import * as fs from 'fs';


export enum LogLevel {
   INFO,
   WARN,
   ERROR
}


// const tagsfile = '.vscode-ctags';
//修改默认路径为.vscode/ctags.out， 与VSCode-C-Call-Hierarchy修改版保持相同
// const tagsfile = '.vscode/ctags.out';//不好使，点击无法跳转呢
const tagsfile = '.vscode/.vscode-ctags';
let tags: ctags.CTags;

class CTagsDefinitionProvider implements vscode.DefinitionProvider {
  public provideDefinition(
    document: vscode.TextDocument,
    position: vscode.Position,
    token: vscode.CancellationToken
  ): vscode.ProviderResult<vscode.Definition> {
    const query = document.getText(document.getWordRangeAtPosition(position));
    return this.resolveDefinitions(query);
  }

  private async resolveDefinitions(query: string): Promise<vscode.Definition> {
    const matches = await tags.lookup(query);
    if (!matches) {
      util.log(`"${query}" has no matches.`);
      return [];
    }
    return matches.map(match => {
      util.log(`"${query}" matches ${match.path}:${match.lineno}`);
      return new vscode.Location(
        vscode.Uri.file(match.path),
        new vscode.Position(match.lineno, 0)
      );
    });
  }
}

class CTagsHoverProvider implements vscode.HoverProvider {
  public provideHover(
    document: vscode.TextDocument,
    position: vscode.Position,
    token: vscode.CancellationToken
  ): vscode.ProviderResult<vscode.Hover> {
    const query = document.getText(document.getWordRangeAtPosition(position));
    return this.resolveHover(query);
  }

  private async resolveHover(query: string): Promise<vscode.Hover | null> {
    const matches = await tags.lookup(query);
    if (!matches) {
      util.log(`"${query}" has no matches.`);
      return null;
    }
    util.log(`"${query}" has ${matches.length} matches.`);
    const summary = matches.map(match => {
      return (
        path.relative(vscode.workspace.rootPath || '', match.path) +
        ':' +
        match.lineno
      );
    });
    return new vscode.Hover(new vscode.MarkdownString(summary.join('  \n')));
  }
}

class CTagsCompletionProvider implements vscode.CompletionItemProvider {
  public provideCompletionItems(
    document: vscode.TextDocument,
    position: vscode.Position,
    token: vscode.CancellationToken,
    context: vscode.CompletionContext
  ): vscode.ProviderResult<vscode.CompletionItem[] | vscode.CompletionList> {
    const prefix = document.getText(document.getWordRangeAtPosition(position));
    return this.resolveCompletion(prefix);
  }

  private async resolveCompletion(
    prefix: string
  ): Promise<vscode.CompletionItem[] | null> {
    const matches = await tags.lookupCompletions(prefix);
    if (!matches) {
      util.log(`"${prefix}" has no matches.`);
      return null;
    }
    util.log(`"${prefix}" has ${matches.length} matches.`);
    return matches.map(match => {
      return new vscode.CompletionItem(match.name);
    });
  }
}

function regenerateArgs(): string[] {
  const config = vscode.workspace.getConfiguration('ctags');
  const excludes = config
    .get<string[]>('excludePatterns', [])
    .map((pattern: string) => {
      return '--exclude=' + pattern;
    })
    .join(' ');
  const languages =
    '--languages=' + config.get<string[]>('languages', ['all']).join(',');
  return [languages, excludes];
}


export function showMessageWindow(msg: string, logLevl: LogLevel = LogLevel.INFO): void {
   const config = vscode.workspace.getConfiguration('ccallhierarchy');
   const canShowMessages = config.get('showMessages');

   if (canShowMessages) {
      switch (logLevl) {
         case LogLevel.INFO:
            vscode.window.showInformationMessage(msg);
            break;
         case LogLevel.WARN:
            vscode.window.showWarningMessage(msg);
            break;
         case LogLevel.ERROR:
            vscode.window.showErrorMessage(msg);
            break;
         default:
            break;
      }
   }
}


export function getWorkspaceRootPath(): string {
   return vscode.workspace.workspaceFolders !== undefined ? vscode.workspace.workspaceFolders[0].uri.fsPath : '';
}

//外部文件，里面包含要索引的目录
export function getExternalIncludeFilePath() {
   const config = vscode.workspace.getConfiguration('ctags');
   const databasePath = `${getWorkspaceRootPath()}/${config.get('external_includedir_file')}`;

   //获取databasePath，判断为文件时，如果父目录不存在，则创建，如果文件不存在则创建 
   if (!fs.existsSync(databasePath)) {
      //判断为文件，则创建父目录
      const parentDir = path.dirname(databasePath);
      if (!fs.existsSync(parentDir)) {
         fs.mkdirSync(parentDir);
      }
      //判断文件不存在，则创建
      console.log(`[ccall]create file: ${databasePath}`);
      showMessageWindow('Please create the external_includedir_file file in the specified directory', LogLevel.INFO);
      if(!fs.existsSync(databasePath))
      {
         fs.writeFileSync(databasePath, '');
      }
   }

   return {
      ExternalIncludeFilePath: `${databasePath}`
   };
}

/**
 * 根据操作系统转换路径分隔符
 * @param inputPath 输入路径
 * @returns 转换后的路径
 */
export function convertPathForOS(inputPath: string): string {
    const platform = process.platform;

    if (platform === 'win32') {
        // 将 Unix 风格路径转换为 Windows 风格路径
        return inputPath.split('/').join('\\');
    } else {
        // 假设其他平台都是类 Unix 系统，将 Windows 风格路径转换为 Unix 风格路径
        return inputPath.split('\\').join('/');
    }
}


function regenerateCTags() {
  const args = regenerateArgs();
  const title =
    args && args.length
      ? `Generating CTags index (${args.join(' ')})`
      : 'Generating CTags index';
  
  //打印下上面的参数
  console.log(`[ctags]regenerateCTags args: ${args}`);
  console.log(`[ctags]regenerateCTags title: ${title}`);
  return vscode.window.withProgress(
    {
      location: vscode.ProgressLocation.Window,
      title
    },
    (progress, token) => {
      return tags.regenerate(regenerateArgs()).catch(err => {
        vscode.window.setStatusBarMessage('Generating CTags failed: ' + err);
      });
    }
  );
}

export function activate(context: vscode.ExtensionContext) {
  util.log('extension activated.');

  tags = new ctags.CTags(vscode.workspace.rootPath || '', tagsfile);
  tags
    .reindex()
    .then(() => {
      vscode.window.setStatusBarMessage('CTags index loaded', 2000);
    })
    .catch(() => {
      return regenerateCTags();
    });

  const definitionsProvider = new CTagsDefinitionProvider();
  vscode.languages.registerDefinitionProvider(
    { scheme: 'file', language: 'cpp' },
    definitionsProvider
  );
  vscode.languages.registerDefinitionProvider(
    { scheme: 'file', language: 'c' },
    definitionsProvider
  );

  const hoverProvider = new CTagsHoverProvider();
  vscode.languages.registerHoverProvider(
    { scheme: 'file', language: 'c' },
    hoverProvider
  );
  vscode.languages.registerHoverProvider(
    { scheme: 'file', language: 'cpp' },
    hoverProvider
  );

  const completionProvider = new CTagsCompletionProvider();
  vscode.languages.registerCompletionItemProvider(
    { scheme: 'file', language: 'c' },
    completionProvider
  );

  vscode.languages.registerCompletionItemProvider(
    { scheme: 'file', language: 'cpp' },
    completionProvider
  );

  const regenerateCTagsCommand = vscode.commands.registerCommand(
    'extension.regenerateCTags',
    () => {
      regenerateCTags();
    }
  );

  context.subscriptions.push(regenerateCTagsCommand);

  vscode.workspace.onDidSaveTextDocument(event => {
    util.log('saved', event.fileName, event.languageId);
    const config = vscode.workspace.getConfiguration('ctags');
    const autoRegenerate = config.get<boolean>('regenerateOnSave');
    if (autoRegenerate) {
      regenerateCTags();
    }
  });
}

export function deactivate() {}
